package com.njbw.train.common.util;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class JwtUtil {

    /**
     * 盐值很重要，不能泄漏，且每个项目都应该不一样，可以放到配置文件中
     */
    private static final String SALT = "Njbw12306";

    /**
     * 生成 token
     */
    public static <T> String createToken(T t) {
        // 过期时间
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime expireDateTime = now.plusSeconds(5);
        // 负载
        Map<String, Object> payload = new HashMap<>();
        // 签发时间
        payload.put(JWTPayload.ISSUED_AT, now);
        // 过期时间
        payload.put(JWTPayload.EXPIRES_AT, expireDateTime);
        // 生效时间
        payload.put(JWTPayload.NOT_BEFORE, now);
        // 内容
        putPayloadContent(payload, t);
        // 生成 token
        String token = JWTUtil.createToken(payload, SALT.getBytes(StandardCharsets.UTF_8));
        log.info("生成 JWT token: {}", token);
        return token;
    }

    /**
     * 拿取 token 中的内容
     */
    public static <T> T parseToken(String token, Class<T> clazz) {
        // 校验不通过直接返回 null
        if (!validate(token)) return null;
        // 解析 token
        JWT jwt = JWTUtil.parseToken(token).setKey(SALT.getBytes(StandardCharsets.UTF_8));
        // 获取载荷
        JSONObject payload = jwt.getPayloads();
        payload.remove(JWTPayload.NOT_BEFORE);
        payload.remove(JWTPayload.ISSUED_AT);
        payload.remove(JWTPayload.EXPIRES_AT);
        // 创建对象
        T content = payload.toBean(new TypeReference<T>() {
        }.getType());
        if (log.isInfoEnabled()) {
            log.info("根据 JWT token 获取原始内容: {}", JSON.toJSONString(content));
        }
        return content;
    }

    /**
     * 校验 token 是否有效
     */
    public static boolean validate(String token) {
        JWT jwt = JWTUtil.parseToken(token).setKey(SALT.getBytes(StandardCharsets.UTF_8));
        // validate 包含了 verify
        boolean validate = jwt.validate(0);
        log.info("JWT token 校验结果：{}", validate);
        return validate;
    }

    private static <T> void putPayloadContent(Map<String, Object> payload, T t) {
        Class<?> clazz = t.getClass();
        // 获取所有字段
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String fieldName = field.getName();
            try {
                Object value = field.get(t);
                payload.put(fieldName, value);
            } catch (IllegalAccessException e) {
                log.info("获取字段值失败：{}", fieldName);
            } finally {
                field.setAccessible(false);
            }
        }
    }
}
